package com.lxb.myforum.pojo

import javax.persistence.*
import javax.validation.constraints.Email
import javax.validation.constraints.Size

import org.springframework.security.core.GrantedAuthority
import org.springframework.security.core.userdetails.UserDetails

import com.fasterxml.jackson.annotation.JsonFormat
import com.fasterxml.jackson.annotation.JsonIgnore

import java.util.Date
import java.util.stream.Collectors

/**
 * 用户信息
 *
 * @author lxb
 */
@Entity
@Table(name = "t_user", uniqueConstraints = [UniqueConstraint(columnNames = arrayOf("username", "phone", "email"))])
data class User @JvmOverloads constructor(

        @Id
        @GeneratedValue(strategy = GenerationType.IDENTITY)
        var id: Long? = null,

        @Size(min = 3, max = 20, message = "用户名长度3~20")
        @Column(name = "username")
        private var username: String? = null,

        @Column(name = "password")
        @Size(min = 5, message = "密码长度至少5位")
        private var password: String? = null,

        @Size(min = 3, max = 20, message = "昵称长度3~20")
        @Column(name = "nickname")
        var nickname: String? = null,

        @Column(name = "email")
        @Email
        var email: String? = null,

        @Column(name = "phone")
        var phone: String? = null,

        @Column(name = "enabled")
        private var enabled: Boolean = true,

        @Column(name = "avatar")
        var avatar: String? = null,

        @Temporal(TemporalType.TIMESTAMP)
        @Column(name = "create_time")
        @get:JsonFormat(pattern = "yyyy-MM-dd HH-mm-ss")
        var createTime: Date? = null,

        @Temporal(TemporalType.TIMESTAMP)
        @Column(name = "update_time")
        @get:JsonFormat(pattern = "yyyy-MM-dd HH-mm-ss")
        var updateTime: Date? = null,

        /**
         * 最后一次密码更新时间
         */
        @Temporal(TemporalType.TIMESTAMP)
        @Column(name = "last_pwd_update_date")
        @get:JsonIgnore
        var lastPwdUpdateDate: Date? = null,

        /**
         * 一个用户拥有多种角色 一个角色可以有多个用户
         * 多对多
         */
        @ManyToMany(fetch = FetchType.EAGER, targetEntity = Role::class)
        @JoinTable(
                name = "t_user_role",
                joinColumns = [JoinColumn(name = "user_id", referencedColumnName = "id")],
                inverseJoinColumns = [JoinColumn(name = "role_id", referencedColumnName = "id")]
        )
        @get:JsonIgnore
        var roles: List<Role>? = null,

        /**
         * 一个用户可以有多个文章，一个文章只能对应一个用户，
         * 连接关系由Article中的user来控制
         */
        @OneToMany(targetEntity = Article::class, mappedBy = "user", fetch = FetchType.LAZY)
        @get:JsonIgnore
        var articles: List<Article>? = null,

        @OneToMany(targetEntity = Comment::class, mappedBy = "user", fetch = FetchType.LAZY)
        @get:JsonIgnore
        var comments: List<Comment>? = null,

        @OneToMany(targetEntity = Post::class, mappedBy = "user", fetch = FetchType.LAZY)
        @get:JsonIgnore
        var posts: List<Post>? = null,

        @OneToMany(targetEntity = Star::class, mappedBy = "user", fetch = FetchType.LAZY)
        private val stars: List<Star>? = null,

        @OneToOne(targetEntity = Setting::class, mappedBy = "user", fetch = FetchType.LAZY)
        var setting: Setting? = null
) : UserDetails {

    override fun getUsername(): String? = username

    @JsonIgnore
    override fun getPassword(): String? = password

    fun setPassword(pwd: String?) {
        this.password = pwd
    }

    fun setUsername(username: String?) {
        this.username = username
    }

    /**
     * 用户的所有角色所拥有的权限
     */
    @JsonIgnore
    override fun getAuthorities(): Collection<GrantedAuthority> {
        return roles?.stream()
                ?.map { it.authorities }
                ?.flatMap { it?.stream() }
                ?.collect(Collectors.toList())
                .orEmpty()
    }

    override fun isAccountNonExpired(): Boolean = true

    override fun isAccountNonLocked(): Boolean = true

    override fun isCredentialsNonExpired(): Boolean = true

    override fun isEnabled(): Boolean = enabled


    companion object {
        private const val serialVersionUID = 1L
    }
}
